package org.budo.support.mvcs;

import org.budo.support.assertion.Assert;
import org.budo.support.java.collection.wrapper.NiceToStringMapWrapper;
import org.budo.support.java.net.util.UrlEncodeUtil;
import org.budo.support.java.security.util.AesUtil;
import org.budo.support.javax.servlet.BudoServletInputStream;
import org.budo.support.lang.util.ArrayUtil;
import org.budo.support.lang.util.IoUtil;
import org.budo.support.lang.util.ListUtil;
import org.budo.support.lang.util.NumberUtil;
import org.budo.support.lang.util.ReflectUtil;
import org.budo.support.lang.util.StringUtil;
import org.budo.support.servlet.util.QueryStringUtil;
import org.budo.support.servlet.util.RequestUtil;
import org.budo.support.servlet.util.ResponseUtil;
import org.budo.support.slf4j.Slf4j;
import org.slf4j.Logger;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletContext;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * TODO 尝试 使用 RequestContextListener 然后去除其他Request
 *
 * @author limingwei2
 * @see org.budo.support.mvcs.MvcsHandlerInterceptor
 * @see org.budo.support.mvcs.MvcsFilter
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public class Mvcs {
    public static final String[] _REMOTE_ADDR_HEADER_NAMES = new String[] { // https://help.aliyun.com/knowledge_detail/40535.html?spm=5176.doc35216.6.599.REcp4N
            "X-Forwarded-For", //
            "x-forwarded-for", //
            "HTTP_X_FORWARDED_FOR", //
            "X-Real-IP", //
            "Proxy-Client-IP", //
            "WL-Proxy-Client-IP" };

    private static final Logger log = Slf4j.getLogger();

    private static final ThreadLocal<HttpServletRequest> _REQUEST = new InheritableThreadLocal<HttpServletRequest>();

    private static final ThreadLocal<HttpServletResponse> _RESPONSE = new InheritableThreadLocal<HttpServletResponse>();

    /**
     * spring 4.0.5 没有这个方法
     */
    private static final boolean _GET_RESPONSE = ReflectUtil.hasMethod(ServletRequestAttributes.class, "getResponse", new Class[0]);

    /**
     * 注意，调用 setThreadLocal 之后，应当调用 removeThreadLocal 作对应的清理
     *
     * @see #removeThreadLocal()
     */
    public static void setThreadLocal(ServletRequest request, ServletResponse response) {
        Assert.notNull(request, "#56 setRequest no null");
        _REQUEST.set((HttpServletRequest) request);

        Assert.notNull(response, "#59 setResponse no null");
        _RESPONSE.set((HttpServletResponse) response);
    }

    public static void removeThreadLocal() {
        _REQUEST.remove();
        _RESPONSE.remove();
    }

    public static Object getHandler() {
        Object handler = MvcsHandlerInterceptor.getHandler();
        if (null == handler) {
            // log.error("#95 getHandler, return null, handler=" + handler);
            return null;
        }

        return handler;
    }

    public static HttpServletResponse getResponse() {
        HttpServletResponse response = null;

        // 优先级顺序
        response = _RESPONSE.get();
        if (null != response) {
            return response;
        }

        response = MvcsHandlerInterceptor.getResponse();
        if (null != response) {
            return response;
        }

        response = MvcsFilter.getResponse();
        if (null != response) {
            return response;
        }

        response = getResponseBySpringHolder();
        if (null != response) {
            return response;
        }

        if (null == response) {
            log.warn("#80 getResponse() returns null, requestAttributes=" + RequestContextHolder.getRequestAttributes());
        }

        return response;
    }

    public static HttpServletRequest getRequest() {
        HttpServletRequest request = null;

        // 优先级顺序
        request = _REQUEST.get();
        if (null != request) {
            return request;
        }

        request = MvcsHandlerInterceptor.getRequest();
        if (null != request) {
            return request;
        }

        request = MvcsFilter.getRequest();
        if (null != request) {
            return request;
        }

        request = getRequestBySpringHolder();
        if (null != request) {
            return request;
        }

        if (null == request) {
            // log.warn("#99 getRequest() returns null, requestAttributes="
            // +RequestContextHolder.getRequestAttributes());
        }

        return request;
    }

    private static HttpServletResponse getResponseBySpringHolder() {
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        if (null != requestAttributes //
                && _GET_RESPONSE) {
            if (requestAttributes instanceof ServletRequestAttributes) {
                return ((ServletRequestAttributes) requestAttributes).getResponse();
            }
        }

        return null;
    }

    /**
     * 获取到的是最初的Request，可能会有Session取不到值的情况，应该作为最低优先级
     */
    private static HttpServletRequest getRequestBySpringHolder() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (null != requestAttributes) {
            if (requestAttributes instanceof ServletRequestAttributes) {
                return ((ServletRequestAttributes) requestAttributes).getRequest();
            }
        }

        return null;
    }

    public static HttpSession getSession() {
        HttpServletRequest request = getRequest();

        if (null == request) {
            log.error("#111 getSession error, request is null, request=" + request);
            return null;
        }

        return request.getSession();
    }

    public static HttpSession getSession(boolean create) {
        return getSession(getRequest(), create);
    }

    public static HttpSession getSession(HttpServletRequest request, boolean create) {
        if (null == request) {
            log.error("#111 getSession error, request is null, request=" + request);
            return null;
        }

        return request.getSession(create);
    }

    public static String getSessionId() {
        HttpSession session = getSession(false);

        if (null == session) {
            log.warn("#122 getSessionId error, session is null" + ", requestURI=" + getRequestURI() + ", handler=" + getHandler());
            return null;
        }

        return session.getId();
    }

    public static Object getSessionAttribute(HttpServletRequest request, String key) {
        HttpSession session = getSession(request, false); // getSession no create

        if (null == session) {
            log.debug("#144 getSessionAttribute error, session is null, key={}, requestURI={}, handler={}, request=", key, getRequestURI(), getHandler(), getRequest());
            return null;
        }

        return session.getAttribute(key);
    }

    public static Object getSessionAttribute(String key) {
        return getSessionAttribute(getRequest(), key);
    }

    public static void removeSessionAttribute(String key) {
        HttpSession session = getSession(false);

        if (null == session) {
            log.warn("#174 removeSessionAttribute error, session is null, key={}, requestURI={}, handler={}, request=", key, getRequestURI(), getHandler(), getRequest());
            return;
        }

        session.removeAttribute(key);
    }

    public static void setRequestAttribute(String key, Object value) {
        HttpServletRequest request = getRequest();

        if (null == request) {
            log.debug("#185 setRequestAttribute error, request is null, key={},value={}", key, value);
            return;
        }

        request.setAttribute(key, value);
    }

    public static Object getRequestAttribute(String key) {
        HttpServletRequest request = getRequest();

        if (null == request) {
            log.debug("#196 getRequestAttribute error, request is null, key={}", key);
            return null;
        }

        Object attribute = request.getAttribute(key);
        return attribute;
    }

    public static Map<String, String[]> getRequestParameterMap() {
        HttpServletRequest request = getRequest();
        return getRequestParameterMap(request);
    }

    public static Map<String, String[]> getRequestParameterMap(ServletRequest request) {
        if (null == request) {
            if (log.isDebugEnabled()) {
                log.debug("#282 getRequestParameterMap error, request is null", new Throwable());
            }

            return null;
        }

        Map<String, String[]> parameterMap = request.getParameterMap();
        return new NiceToStringMapWrapper(parameterMap);
    }

    public static String[] getRequestParameterValues(String key) {
        HttpServletRequest request = getRequest();
        return getRequestParameterValues(request, key);
    }

    public static String getRequestParameter(String key) {
        HttpServletRequest request = getRequest();
        return getRequestParameter(request, key);
    }

    public static String[] getRequestParameterValues(HttpServletRequest request, String key) {
        if (null == request || null == key) {
            log.error("#277 getRequestParameter error, request is null, request=" + request + ", key=" + key);
            return null;
        }

        String[] values = request.getParameterValues(key);
        return values;
    }

    public static String getRequestParameter(HttpServletRequest request, String key) {
        if (null == request) {
            if (log.isDebugEnabled()) {
                log.debug("#311 getRequestParameter error, request is null, request=" + request + ", key=" + key, new Throwable());
            }

            return null;
        }

        if (null == key) {
            log.error("#316 getRequestParameter error, request is null, request=" + request + ", key=" + key);
            return null;
        }

        String value = request.getParameter(key);
        if (!StringUtil.isEmpty(value)) {
            return value;
        }

        // 项目中有出现过 getParameter 获取不到，但 queryString 有值的情况，不知具体原因
        String queryString = request.getQueryString();
        Map<String, String[]> queryStringMap = QueryStringUtil.queryStringToMap(queryString);
        String[] queryValue = queryStringMap.get(key);
        if (!ArrayUtil.isEmpty(queryValue)) {
            return queryValue[0];
        }

        return value;
    }

    public static Map<String, String[]> getRequestHeaderMap(ServletRequest request) {
        if (null == request) {
            if (log.isDebugEnabled()) {
                log.debug("#247 getRequestHeaderMap error, request is null, request=" + request, new Throwable());
            }

            return null;
        }

        Map<String, String[]> map = new HashMap<String, String[]>();

        Enumeration<String> enumeration = ((HttpServletRequest) request).getHeaderNames();
        while (enumeration.hasMoreElements()) {
            String key = enumeration.nextElement();
            String[] values = getRequestHeaders((HttpServletRequest) request, key);
            map.put(key, values);
        }

        return new NiceToStringMapWrapper(map);
    }

    public static Map<String, String[]> getRequestHeaderMap() {
        HttpServletRequest request = getRequest();
        return getRequestHeaderMap(request);
    }

    public static String[] getRequestHeaders(HttpServletRequest request, String key) {
        if (null == request) {
            log.error("#273 getRequestHeaders error, request is null, key=" + key);
            return null;
        }

        List<String> values = new ArrayList<String>();

        Enumeration<String> enumeration = request.getHeaders(key);
        while (enumeration.hasMoreElements()) {
            values.add(enumeration.nextElement());
        }

        return values.toArray(new String[values.size()]);
    }

    public static String[] getRequestHeaders(String key) {
        HttpServletRequest request = getRequest();
        return getRequestHeaders(request, key);
    }

    public static Map<String, Object> getRequestAttributeMap(HttpServletRequest request) {
        if (null == request) {
            log.error("#289 getRequestAttributeMap error, request is null");
            return null;
        }

        Enumeration<String> enumeration = request.getAttributeNames();
        Map<String, Object> map = new HashMap<String, Object>();

        while (enumeration.hasMoreElements()) {
            String key = enumeration.nextElement();
            Object value = request.getAttribute(key);
            map.put(key, value);
        }

        return new NiceToStringMapWrapper(map);
    }

    public static Map<String, Object> getRequestAttributeMap() {
        HttpServletRequest request = getRequest();
        return getRequestAttributeMap(request);
    }

    public static String getRequestBody() {
        HttpServletRequest request = getRequest();
        return getRequestBody(request);
    }

    public static ServletInputStream getInputStream(ServletRequest request) {
        if (null == request) {
            log.error("#393 getInputStream, request=" + request);
            return null;
        }

        // 暂不要可重复读
        return getInputStream_original(request);
    }

    /**
     * @see org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload#isMultipartContent(HttpServletRequest)
     */
    public static Boolean isMultipart(ServletRequest request) {
        if (null == request) {
            log.error("#406 isMultipart, request=" + request);
            return false;
        }

        HttpServletRequest req = (HttpServletRequest) request;
        if (!"POST".equalsIgnoreCase(req.getMethod())) {
            return false;
        }

        String contentType = req.getContentType();
        if (null == contentType) {
            return false;
        }

        if (contentType.toLowerCase(Locale.ENGLISH).startsWith("multipart/")) {
            return true;
        }

        return false;
    }

    /**
     * @deprecated
     */
    public static ServletInputStream getInputStream_repeatable(ServletRequest request) {
        byte[] bytes = (byte[]) request.getAttribute("_inputStreamBytes");
        if (null != bytes) {
            return new BudoServletInputStream(bytes);
        }

        ServletInputStream inputStream = getInputStream_original(request);
        bytes = IoUtil.toByteArray(inputStream);
        request.setAttribute("_inputStreamBytes", bytes);

        return new BudoServletInputStream(bytes);
    }

    public static String getRequestBody(ServletRequest request) {
        if (null == request) {
            return null;
        }

        // 这里做 _requestBody 的缓存 inputStream 未做
        String _requestBody = (String) request.getAttribute("_requestBody");
        if (null != _requestBody) {
            return _requestBody;
        }

        try {
            ServletInputStream inputStream = getInputStream(request);
            String requestBody = IoUtil.toString(inputStream, "UTF-8");

            request.setAttribute("_requestBody", requestBody);
            return requestBody;
        } catch (Throwable e) {
            if (("" + e).contains("IOException: Stream closed")) {
                log.warn("#474 " + e + ", request=" + request + ", requestURI=" + Mvcs.getRequestURI(request) + ", handler=" + Mvcs.getHandler() + ", headers=" + Mvcs.getRequestHeaderMap(request) + ", e=" + e);
                return null;
            }

            log.error("#478 " + e + ", request=" + request + ", requestURI=" + Mvcs.getRequestURI(request) + ", handler=" + Mvcs.getHandler() + ", headers=" + Mvcs.getRequestHeaderMap(request) + ", e=" + e);
            return null;
        }
    }

    public static ServletInputStream getInputStream_original(ServletRequest request) {
        if (null == request) {
            log.warn("#485 getInputStream_original return null, request=" + request);
            return null;
        }

        try {
            return request.getInputStream();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    public static ServletInputStream getInputStream() {
        HttpServletRequest request = getRequest();
        return getInputStream(request);
    }

    public static ServletOutputStream getResponseOutputStream() {
        HttpServletResponse response = getResponse();
        return getResponseOutputStream(response);
    }

    public static ServletOutputStream getResponseOutputStream(HttpServletResponse response) {
        try {
            return response.getOutputStream();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static String getRequestURI() {
        HttpServletRequest request = getRequest();
        if (null == request) {
            if (log.isDebugEnabled()) {
                log.debug("#517 getRequestURI return null, request=" + request, new Throwable());
            }

            return null;
        }

        return request.getRequestURI();
    }

    public static String getScheme() {
        HttpServletRequest request = getRequest();
        if (null == request) {
            log.error("#527 getRequestURI return null, request=" + request);
            return null;
        }

        return request.getScheme();
    }

    public static Integer getServerPort() {
        HttpServletRequest request = getRequest();
        return getServerPort(request);
    }

    public static Integer getServerPort(HttpServletRequest request) {
        if (null == request) {
            log.error("#521 getServerPort, request=" + request);
            return null;
        }

        Integer originPort = getRequestHeaderInteger(request, "Origin-port", null);
        if (null != originPort) {
            return originPort;
        }

        return request.getServerPort();
    }

    public static String getServerName() {
        HttpServletRequest request = getRequest();
        return getServerName(request);
    }

    public static String getServerName(HttpServletRequest request) {
        if (null == request) {
            if (log.isDebugEnabled()) {
                log.debug("#563 getServerName error, request is null", new Throwable());
            }

            return null;
        }

        String originHost = getRequestHeader(request, "Origin-host");
        if (StringUtil.isNotBlank(originHost)) {
            return originHost;
        }

        try {
            return request.getServerName();
        } catch (Throwable e) {
            throw new IllegalStateException("#527 getServerName error, request=" + request, e);
        }
    }

    public static void invalidateSession() {
        HttpSession session = getSession(false);

        if (null == session) {
            log.warn("#387 invalidateSession error, session is null");
            return;
        }

        session.invalidate();
    }

    public static String getRequestContextPath() {
        HttpServletRequest request = getRequest();
        return null == request ? null : request.getContextPath();
    }

    public static String getRequestContextPath(HttpServletRequest request) {
        return null == request ? null : request.getContextPath();
    }

    public static StringBuffer getRequestURL() {
        HttpServletRequest request = getRequest();
        return null == request ? null : request.getRequestURL();
    }

    public static String getReferer(HttpServletRequest request) {
        if (null == request) {
            return null;
        }

        String header = request.getHeader("referer");
        if (!StringUtil.isEmpty(header)) {
            return header;
        }

        return request.getHeader("Referer");
    }

    public static String getReferer() {
        HttpServletRequest request = getRequest();
        return getReferer(request);
    }

    public static String getRequestedSessionId() {
        HttpServletRequest request = getRequest();
        return null == request ? null : request.getRequestedSessionId();
    }

    public static void sendError(Integer statusCode, String descriptiveMessage) {
        HttpServletResponse response = getResponse();
        ResponseUtil.sendError(response, statusCode, descriptiveMessage);
    }

    public static Long getRequestParameterLong(String key) {
        String value = getRequestParameter(key);
        return NumberUtil.toLong(value);
    }

    public static String getRequestParameter(String key, String defaultValue) {
        String value = getRequestParameter(key);
        if (StringUtil.isEmpty(value)) {
            return defaultValue;
        }

        return value;
    }

    public static void redirect(String location) {
        HttpServletResponse response = getResponse();

        if (null == response) {
            log.error("#458 forward error, response=" + response + ", location=" + location);
            return;
        }

        ResponseUtil.sendRedirect(response, location);
    }

    public static void forward(String path) {
        HttpServletRequest request = getRequest();
        HttpServletResponse response = getResponse();

        if (null == request || null == response) {
            log.error("#470 forward error, request=" + request + ", response=" + response + ", path=" + path);
            return;
        }

        RequestUtil.forward(path, request, response);
    }

    public static Boolean isPost() {
        HttpServletRequest request = getRequest();

        if (null == request) {
            log.error("#482 forward error, request=" + request);
            return false;
        }

        String method = request.getMethod();
        return StringUtil.equalsIgnoreCase("POST", method);
    }

    public static ServletContext getServletContext() {
        HttpServletRequest request = getRequest();
        return getServletContext(request);
    }

    public static ServletContext getServletContext(HttpServletRequest request) {
        if (null == request) {
            log.error("#494 getServletContext error, request is null");
            return null;
        }

        HttpSession session = request.getSession();
        return session.getServletContext();
    }

    public static InputStream getResourceAsStream(String path) {
        ServletContext servletContext = getServletContext();

        if (null == servletContext) {
            log.error("#505 getResourceAsStream error, servletContext is null, path=" + path);
            return null;
        }

        return servletContext.getResourceAsStream(path);
    }

    public static String getRealPath(String path) {
        ServletContext servletContext = getServletContext();

        if (null == servletContext) {
            log.error("#516 getRealPath error, servletContext is null, path=" + path);
            return null;
        }

        return servletContext.getRealPath(path);
    }

    public static Object getServletContextAttribute(String name) {
        ServletContext servletContext = getServletContext();

        if (null == servletContext) {
            log.error("#527 getServletContextAttribute error, servletContext is null, name=" + name);
            return null;
        }

        return servletContext.getAttribute(name);
    }

    public static void setServletContextAttribute(String key, Object value) {
        ServletContext servletContext = getServletContext();

        if (null == servletContext || null == key) {
            log.error("#538 setServletContextAttribute error, servletContext or key is null, key=" + key + ", value=" + value);
            return;
        }

        servletContext.setAttribute(key, value);
    }

    public static void removeServletContextAttribute(String key) {
        ServletContext servletContext = getServletContext();

        if (null == servletContext || null == key) {
            log.error("#549 removeServletContextAttribute error, servletContext or key is null, key=" + key);
            return;
        }

        servletContext.removeAttribute(key);
    }

    public static String getMethod(ServletRequest request) {
        if (null == request) {
            log.error("#560 getMethod error, request is null");
            return null;
        }

        return ((HttpServletRequest) request).getMethod();
    }

    public static String getMethod() {
        return getMethod(getRequest());
    }

    public static ServletOutputStream getOutputStream() {
        HttpServletResponse response = getResponse();

        if (null == response) {
            log.error("#574 getOutputStream error, request is null");
            return null;
        }

        ServletOutputStream outputStream = getOutputStream(response);
        return outputStream;
    }

    public static ServletOutputStream getOutputStream(ServletResponse response) {
        try {
            return response.getOutputStream();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static String getRequestURI(ServletRequest request) {
        if (null == request) {
            log.error("#589 getRequestURI error, request is null");
            return null;
        }

        return ((HttpServletRequest) request).getRequestURI();
    }

    public static String requestToString(ServletRequest request) {
        return requestToString(request, true);
    }

    private static String requestToString(ServletRequest request, Boolean withCookie) {
        if (null == request) {
            return "request=null";
        }

        String str = "request=" + request.getClass().getName() + "@" + Integer.toHexString(request.hashCode()) //
                + ", URI=" + getRequestURI(request);

        if (null != withCookie && withCookie) {
            Cookie[] cookies = getRequestCookies((HttpServletRequest) request);
            str += "cookies=" + cookieToString(cookies);
        }

        if (request instanceof HttpServletRequestWrapper) {
            HttpServletRequestWrapper wrapper = (HttpServletRequestWrapper) request;
            str += ", wrappered=" + requestToString(wrapper.getRequest(), false);
        }

        return str;
    }

    private static String cookieToString(Cookie[] cookies) {
        if (null == cookies) {
            return "null";
        }

        String str = "[";
        for (int i = 0; i < cookies.length; i++) {
            if (i > 0) {
                str += ",";
            }

            if (null == cookies[i]) {
                str += "null";
            } else {
                str += cookies[i].getName() + "=" + cookies[i].getValue();
            }
        }
        str += "]";
        return str;
    }

    public static String getUserAgent() {
        return getRequestHeader("User-Agent");
    }

    public static String getUserAgent(ServletRequest request) {
        return getRequestHeader(request, "User-Agent");
    }

    public static List<String> getRequestHeaderNames(ServletRequest request) {
        Enumeration<String> enumeration = ((HttpServletRequest) request).getHeaderNames();
        return ListUtil.enumerationToList(enumeration);
    }

    public static String getRequestHeader(ServletRequest request, String key) {
        if (null == request || null == key || !(request instanceof HttpServletRequest)) {
            log.error("#728 getRequestHeader error, request or key is null, request=" + request + ", key=" + key);
            return null;
        }

        String value = ((HttpServletRequest) request).getHeader(key);
        if (StringUtil.isNotNullOrEmpty(value)) {
            return value;
        }

        List<String> requestHeaderNames = getRequestHeaderNames(request);
        for (String name : requestHeaderNames) {
            if (StringUtil.equalsIgnoreCase(name, key)) {
                String val = ((HttpServletRequest) request).getHeader(name);
                log.info("#740 getRequestHeader, name=" + name + ", key=" + key + ", val=" + val);
                return val;
            }
        }

        log.debug("#849 getRequestHeader, request={}, key={}, requestHeaderNames={}", request, key, requestHeaderNames);
        return null;
    }

    public static String getRequestHeader(String key) {
        HttpServletRequest request = getRequest();
        return getRequestHeader(request, key);
    }

    public static Map<String, Object> getServletContextAttributes(ServletContext servletContext) {
        Map<String, Object> attributes = new HashMap<String, Object>();
        Enumeration<String> attributeNames = servletContext.getAttributeNames();
        while (attributeNames.hasMoreElements()) {
            String attributeName = attributeNames.nextElement();
            attributes.put(attributeName, servletContext.getAttribute(attributeName));
        }
        return attributes;
    }

    /**
     * 获取请求者IP
     */
    public static String getRemoteAddr(ServletRequest request, String[] headerNames) {
        if (null == request || null == headerNames || headerNames.length < 1) {
            return null;
        }

        for (String headerName : headerNames) {
            String ip = ((HttpServletRequest) request).getHeader(headerName);
            if (null == ip || ip.trim().isEmpty() || "unknown".equalsIgnoreCase(ip) || "null".equalsIgnoreCase(ip)) {
                continue;
            }

            String _var = ip.split(",")[0];
            if (null == _var || _var.trim().isEmpty() || "unknown".equalsIgnoreCase(_var) || "null".equalsIgnoreCase(_var)) {
                continue;
            }

            return _var; // 阿里云
        }

        return null;
    }

    public static String getRemoteAddr(ServletRequest request) {
        if (null == request) {
            return null;
        }

        // from header
        String remoteAddr = getRemoteAddr(request, _REMOTE_ADDR_HEADER_NAMES);
        if (null == remoteAddr || remoteAddr.trim().isEmpty() || "null".equalsIgnoreCase(remoteAddr) || "unknown".equalsIgnoreCase(remoteAddr)) {
            return request.getRemoteAddr();
        }

        return remoteAddr;
    }

    public static String getRemoteAddr() {
        HttpServletRequest request = getRequest();
        return getRemoteAddr(request);
    }

    public static Integer getRequestHeaderInteger(String key) {
        return getRequestHeaderInteger(getRequest(), key, null);
    }

    public static Integer getRequestHeaderInteger(ServletRequest request, String key, Integer defaultValue) {
        String value = getRequestHeader(request, key);

        if (StringUtil.isEmpty(value) || value.trim().isEmpty() || "null".equalsIgnoreCase(value)) {
            return defaultValue;
        }

        try {
            return Integer.valueOf(value);
        } catch (Throwable e) {
            String message = "#758 getRequestHeaderInteger error, key=" + key + ", value=" + value //
                    + ", UserAgent=" + getUserAgent() + ", RemoteAddr=" + getRemoteAddr() //
                    + ", ServerName=" + getServerName() + ", RequestURI=" + getRequestURI() //
                    + ", e=" + e;

            log.error(message, e);

            throw new RuntimeException(message);
        }
    }

    public static Integer getRequestParameterInteger(ServletRequest request, String key, Integer defaultValue) {
        String value = getRequestParameter(key);

        if (StringUtil.isEmpty(value) || value.trim().isEmpty() //
                || "null".equalsIgnoreCase(value) || "undefined".equalsIgnoreCase(value)) {
            return defaultValue;
        }

        try {
            return new Integer(value);
        } catch (Throwable e) {
            String message = "#793 getRequestParameterInteger error, key=" + key + ", value=" + value //
                    + ", UserAgent=" + getUserAgent() + ", RemoteAddr=" + getRemoteAddr() //
                    + ", ServerName=" + getServerName() + ", RequestURI=" + getRequestURI() //
                    + ", e=" + e;

            log.error(message, e);

            throw new RuntimeException(message);
        }
    }

    public static Integer getRequestParameterInteger(String key, Integer defaultValue) {
        HttpServletRequest request = getRequest();
        return getRequestParameterInteger(request, key, defaultValue);
    }

    public static Integer getRequestParameterInteger(HttpServletRequest request, String key) {
        return getRequestParameterInteger(request, key, null);
    }

    public static Integer getRequestParameterInteger(String key) {
        HttpServletRequest request = getRequest();
        return getRequestParameterInteger(request, key, null);
    }

    public static void addCookie(HttpServletResponse response, String name, String value) {
        addCookie(response, name, value, null);
    }

    /**
     * @param expiry 秒
     */
    public static void addCookie(String name, String value, Integer expiry) {
        HttpServletResponse response = getResponse();
        addCookie(response, name, value, expiry);
    }

    public static void addCookie(String name, String value) {
        addCookie(name, value, null);
    }

    public static void removeCookie(String name) {
        HttpServletResponse response = getResponse();
        removeCookie(response, name);
    }

    /**
     * cookie.path 不同的，不能删除
     */
    public static void removeCookie(HttpServletResponse response, String name) {
        if (null == response || StringUtil.isEmpty(name)) {
            log.error("#762 removeCookie error, response=" + response + ", name=" + name);
            return;
        }

        String value = "remove-cookie-" + name;
        Cookie cookie = new Cookie(name, value);
        cookie.setPath("/");
        cookie.setMaxAge(0);
        cookie.setHttpOnly(false);
        response.addCookie(cookie);
    }

    public static void addCookie(HttpServletResponse response, String name, String value, Integer expiry) {
        if (null == response) {
            log.error("#734 addCookie error, response=" + response + ", name=" + name + ", value=" + value);
            return;
        }

        Cookie cookie = new Cookie(name, value);
        cookie.setPath("/");
        if (null != expiry) {
            cookie.setMaxAge(expiry);
        }

        response.addCookie(cookie);
    }

    public static void invalidateCookie(HttpServletRequest request, HttpServletResponse response) {
        Cookie[] cookies = getRequestCookies(request);
        if (ArrayUtil.isEmpty(cookies)) {
            return;
        }

        for (Cookie cookie : cookies) {
            if (null != cookie) {
                String name = cookie.getName();
                removeCookie(response, name);
            }
        }
    }

    public static void invalidateCookie() {
        HttpServletRequest request = getRequest();
        HttpServletResponse response = getResponse();
        invalidateCookie(request, response);
    }

    public static Cookie getRequestCookie(String name) {
        HttpServletRequest request = getRequest();
        return getRequestCookie(request, name);
    }

    public static String getRequestCookieValue(String name) {
        Cookie cookie = getRequestCookie(name);
        if (null == cookie) {
            return null;
        }

        return UrlEncodeUtil.decode(cookie.getValue());
    }

    public static String getRequestCookieValue(HttpServletRequest request, String name) {
        Cookie cookie = getRequestCookie(request, name);
        return null == cookie ? null : cookie.getValue();
    }

    public static Cookie[] getRequestCookies(HttpServletRequest request) {
        if (null == request) {
            log.error("#598 getRequestCookies error, request is null");
            return null;
        }

        return request.getCookies();
    }

    public static Cookie[] getRequestCookies() {
        HttpServletRequest request = getRequest();
        return getRequestCookies(request);
    }

    public static Cookie getRequestCookie(HttpServletRequest request, String name) {
        Cookie[] cookies = getRequestCookies(request);

        if (null == cookies) {
            return null;
        }

        for (Cookie cookie : cookies) {
            if (null == cookie) {
                continue;
            }

            String _name = cookie.getName();
            if (name.equals(_name)) {
                return cookie;
            }
        }

        return null;
    }

    public static Integer getRequestCookieValueInteger(String name) {
        return getRequestCookieValueInteger(name, null);
    }

    public static Integer getRequestCookieValueInteger(String name, Integer defaultValue) {
        String value = getRequestCookieValue(name);

        if (StringUtil.isEmpty(value) || value.trim().isEmpty() || "null".equalsIgnoreCase(value)) {
            return defaultValue;
        }

        try {
            return new Integer(value);
        } catch (Throwable e) {
            String message = "#772 getRequestCookieValueInteger error, name=" + name + ", value=" + value //
                    + ", UserAgent=" + getUserAgent() + ", RemoteAddr=" + getRemoteAddr() //
                    + ", ServerName=" + getServerName() + ", RequestURI=" + getRequestURI() //
                    + ", e=" + e;

            log.error(message, e);

            throw new RuntimeException(message);
        }
    }

    /**
     * Content-disposition: attachment;
     * filename="6PKLToWKRJfkdiEggo_IoRo4pI24vQzDk89VtxCqHOF59NkmEsEJpayrn4_BAirJ.jpg"
     * Content-disposition: attachment; filename="MEDIA_ID.jpg"
     */
    public static String getFilenameFromContentDisposition(String contentDisposition) {
        if (StringUtil.isEmpty(contentDisposition)) {
            return null;
        }

        String fromString = "filename=\"";
        int fromStringLength = fromString.length();
        int fromIndex = contentDisposition.indexOf(fromString);
        if (fromIndex < 1) {
            return null;
        }

        int fromStringEndIndex = fromIndex + fromStringLength;
        int engIndex = contentDisposition.indexOf("\"", fromStringEndIndex);

        if (engIndex >= 0) {
            return contentDisposition.substring(fromStringEndIndex, engIndex);
        }

        return contentDisposition.substring(fromStringEndIndex);
    }

    public static String getQueryString() {
        return QueryStringUtil.mapToQueryString(Mvcs.getRequestParameterMap());
    }

    public static void setRequestAttributeMap(Map attributeMap) {
        HttpServletRequest request = getRequest();
        if (null == attributeMap || attributeMap.isEmpty() || null == request) {
            log.error("#987 attributeMap=" + attributeMap + ", request=" + request);
            return;
        }

        Set entrySet = attributeMap.entrySet();
        for (Object each : entrySet) {
            Entry entry = (Entry) each;
            Object value = entry.getValue();
            String key = (String) entry.getKey();
            setRequestAttribute(key, value);
        }
    }

    public static Object getRequestAttribute(String key, Object defaultValue) {
        Object val = getRequestAttribute(key);
        if (null == val) {
            return defaultValue;
        }
        return val;
    }

    public static void redirect302(HttpServletResponse response, String location) {
        if (null == response) {
            log.error("#1054 redirect302 to " + location + " error, response=" + response);
            return;
        }

        response.setStatus(302);
        response.setHeader("Location", location);
    }

    public static void redirect302(String location) {
        HttpServletResponse response = getResponse();
        redirect302(response, location);
    }

    /**
     * @see #getRequestParameterMap(ServletRequest)
     */
    public static Map<String, Object> getRequestParameterMap_1(HttpServletRequest request) {
        Enumeration<String> parameterNames = request.getParameterNames();
        if (parameterNames == null) {
            return null;
        }

        Map<String, Object> requestParameterMap = new HashMap<String, Object>();

        while (parameterNames.hasMoreElements()) {
            String parameterName = (String) parameterNames.nextElement();
            String parameterValue = request.getParameter(parameterName);
            requestParameterMap.put(parameterName, parameterValue);
        }

        return requestParameterMap;
    }

    public static String schemaDomain(String url) {
        if (null == url) {
            return null;
        }

        int a = url.indexOf("//");
        if (a < 0) {
            return url;
        }

        int b = url.indexOf('/', a + 2);
        if (b <= 0) {
            return url;
        }

        return url.substring(0, b);
    }

    public static String getContentType() {
        return getRequestHeader("Content-Type");
    }

    public static Throwable getError() {
        return (Throwable) getRequestAttribute("_error");
    }

    public static void setError(Throwable error) {
        setRequestAttribute("_error", error);
    }

    public static InputStream getRequestBodyAsStream() {
        String requestBody = getRequestBody();
        if (null == requestBody) {
            return null;
        }

        return new ByteArrayInputStream(requestBody.getBytes());
    }

    public static InputStream getRequestBodyAsStream(HttpServletRequest request) {
        String requestBody = getRequestBody(request);
        if (null == requestBody) {
            return null;
        }

        return new ByteArrayInputStream(requestBody.getBytes());
    }

    public static Map<String, Object> getSessionAttributeMap() {
        HttpServletRequest request = getRequest();
        return getSessionAttributeMap(request);
    }

    public static Map<String, Object> getSessionAttributeMap(HttpServletRequest request) {
        if (null == request) {
            log.error("#1124 getSessionAttributeMap error, request is null, request=" + request);
            return null;
        }

        HttpSession session = request.getSession(false);
        if (null == session) {
            log.warn("#1130 getSessionAttributeMap error, session is null, request=" + request);
            return null;
        }

        Map<String, Object> map = new HashMap<String, Object>();
        Enumeration<String> enumeration = session.getAttributeNames();
        while (enumeration.hasMoreElements()) {
            String key = enumeration.nextElement();
            Object value = session.getAttribute(key);
            map.put(key, value);
        }

        return new NiceToStringMapWrapper(map);
    }

    public static void setSessionAttribute(String key, Object value) {
        HttpSession session = getSession();

        if (null == session) {
            log.error("#1138 setSessionAttribute error, session is null, key=" + key + ", value=" + value);
            return;
        }

        session.setAttribute(key, value);
    }

    public static void setSessionAttribute(ServletRequest request, String key, String value) {
        if (null == request) {
            log.error("#1147 setSessionAttribute error, session is null, key=" + key + ", value=" + value);
            return;
        }

        HttpSession session = ((HttpServletRequest) request).getSession();
        session.setAttribute(key, value);
    }

    public static String getServerName(ServletRequest request) {
        if (null == request) {
            return null;
        }

        return request.getServerName();
    }

    public static String getContentType(HttpServletRequest request) {
        return getRequestHeader(request, "Content-Type");
    }

    public static void setContentType(String contentType) {
        HttpServletResponse response = getResponse();
        setContentType(response, contentType);
    }

    public static void setContentType(HttpServletResponse response, String contentType) {
        if (null == response) {
            log.error("#1234 setContentType error, response is null, contentType=" + contentType);
        }

        response.setContentType(contentType);
    }

    public static Boolean isHttps() {
        String scheme = getScheme();
        return null != scheme && scheme.contains("https");
    }

    public static Integer[] getRequestParameterIntegerValues(String key) {
        String[] values = getRequestParameterValues(key);
        return NumberUtil.toIntegerArray(values);
    }

    public static List<Integer> getRequestParameterIntegerList(String key) {
        Integer[] values = getRequestParameterIntegerValues(key);
        if (null == values) {
            return null;
        }

        return new ArrayList<Integer>(Arrays.asList(values));
    }

    public static HttpSession getSession(HttpServletRequest request) {
        if (null == request) {
            log.error("#1373 getSession return null, request=" + request);
            return null;
        }

        return request.getSession();
    }

    public static String getRequestBodyWithAesDecrypt(String decryptKey) throws Exception {
        final String cipherText = getRequestBody();
        return AesUtil.decrypt(cipherText, decryptKey);
    }
}
